/*
 * Code for blog.techhead.biz
 * Distributed under BSD-style license
 */

package biz.techhead.funcy.ref;

import biz.techhead.funcy.Func;
import biz.techhead.funcy.FuncE;
import biz.techhead.funcy.Funcs;

/**
 * @author Jonathan Hawkes <jhawkes at techhead.biz>
 */
public abstract class LazyReference<T> implements Reference<T> {

    public LazyReference() {
        this( References.<T>strong() );
    }

    public LazyReference(FuncE<? extends Reference<T>,
                            T, ? extends Throwable> refMaker) {
        this.refMaker = Funcs.asFunc(refMaker);
    }

    public T get() {
        T v;
        Reference<T> r = ref;
        // a variation on double-checked locking
        // no longer broken as of Java 1.5
        if ( (r == null) || ((v=r.get()) == null) ) {
            // Possibly dangerous to synchronize on 'this'
            // as it allows for side effects if external code
            // synchronized on this Reference object.
            // Try replacing with a dedicated lock Object
            // or ReentrantLock
            synchronized (this) {
                r = ref;
                if ( (r == null) || ((v=r.get()) == null) ) {
                    v = initValue();
                    r = ref = refMaker.call(v);
                }
            }
        }
        return v;
    }

    abstract protected T initValue();

    private volatile Reference<T> ref;
    private final Func<? extends Reference<T>,T> refMaker;
}
